#!/usr/bin/env bash
# fin.sh - Funtoo installation script
#
## License
#
# Copyright (c) 2021 Damon Kwok <damon-kwok@outlook.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
## About
#
# While this script does assist with installing Funtoo, it is NOT a
# replacement for reading and understanding the Funtoo Handbook for
# your system.  See https://funtoo.org/get-started/ for information on
# getting started with Funtoo.
#
# This file uses inline documentation whenever possible to preserve
# context, situational or otherwise.  Please read the entire file
# before continuing to ensure you are aware of how this script
# functions, as undesired behaviors for your use-case may exist.
#
# This script does NOT automatically partition your disk(s).  You MUST
# partition and mount your disk(s) before running this script.
#
##

set -e -o pipefail

alias fold="fold -s -w ${COLUMNS:-$(stty size|awk '{print $2}')}"
alias make="make -s -j $(grep -c ^processor /proc/cpuinfo)"

# Width-respecting print
OS_NAME="Funtoo"
function print() {
  echo "<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>"
  echo "<> ${OS_NAME}: $@" | fold
  #echo "<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>"
  echo ""
}

# This section describes variables that will define the resulting
# system.  Some are specific to this script, though others should look
# familiar, aside from their prefix.
#
#   FIN_CONFIG_URL
#
#       The base URL where the source of this repository (or your fork)
#       is available.  This allows this script to download all needed
#       configuration files rather than you having to retrieve them
#       manually.
#
#   FIN_HOSTNAME
#
#       The hostname to set on the resulting system after the
#       installation has completed.
#
#   FIN_LOCALE
#
#       The LOCALE to set based on your preference.  All language
#       defaults such as this default to some variant of en_US.UTF-8
#       for simplicity of design.
#
#   FIN_VIDEO_CARDS
#
#       The value of VIDEO_CARDS that will be set in
#       '/etc/portage/make.conf', which may either be left blank or be
#       configured as mentioned in the following url:
#
#       https://wiki.funtoo.org/wiki//etc/portage/make.conf#VIDEO_CARDS
#
#       You most likely should use "amdpu", "intel", or "nvidia".
#
#   FIN_TIMEZONE
#
#       The timezone to set on the resulting system after the
#       installation has completed.  This is set to America/New_York
#       by default for simplicity of design.

FIN_CONFIG_URL="https://gitee.com/damon-kwok/fin/raw/main"
FIN_HOSTNAME="funtoo"
FIN_LOCALE="en_US.UTF-8 UTF-8"
FIN_TIMEZONE="Asia/Shanghai"
FIN_VIDEO_CARDS="intel nvidia radeon vesa virtualbox vmware"


# This section describes how the kernel will be built, and whether the
# user will be prompted to configure their kernel. The two mentioned
# variables may be used together to suit a variety of use-cases.
#
#   FIN_KERNEL_AUTOBUILD
#
#     When 'true', this implies that the user does not want to perform
#     any manual configuration of the kernel, regardless of whether a
#     configuration file was provided. '$ make menuconfig' will NOT be
#     run before compiling the kernel.
#
#     When any value OTHER than 'true', this implies that the use DOES
#     want to perform manual configuration on the kernel, regardless
#     of whether a configuration file was provided. '$ make menuconfig'
#     will be run before compiling the kernel.

FIN_KERNEL_AUTOBUILD="false"


# TODO: I would like this section to be where a user defines their
# partition schema, specifically for configuring GRUB2.
#
#   FIN_PARTITION_BOOT
#
#       Missing description
#
#   FIN_PARTITION_SWAP
#
#       Missing description
#
#   FIN_PARTITION_SWAPFILE_SIZE
#
#       The size of the swapfile to create.  This is only used if
#       FIN_PARTITION_SWAP is set to "/swapfile", and defaults to 4G.

FIN_PARTITION_BOOT="/dev/sda"
FIN_PARTITION_SWAP="/dev/sda2"
FIN_PARTITION_SWAPFILE_SIZE="20G"
FIN_PARTITION_ROOT="/dev/sda3"

# This section determines the current system architecture, which later
# is used to download the correct Stage 3 archive.  Depending on the
# architecture, there may be implications in which directory we should
# retrieve our Stage 3 from.

case "$(uname -m)" in

  i686|x86|x86_32)
    FIN_CPU_DIR="x86-32bit"
    FIN_CPU_ARCH="i686"
    ;;

  amd64|x86_64)
    FIN_CPU_DIR="x86-64bit"
    FIN_CPU_ARCH="intel64-skylake"
    ;;

  *)
    print "error: your architecture is not yet defined." \
          "Submit an issue with the output of 'uname -m' to" \
          "https://github.com/jcmdln/fin so it may be reviewed"
    print "Exiting..."
    exit 1
esac

OUT_DEPS() {
  if [ -v "$FIN_PARTITION_BOOT" ]; then
    print "error: FIN_PARTITION_BOOT is not set!"
    print "please ensure you have partitioned and mounted" \
          "your disks, as well as updated the variables associated" \
          "with the required partitions."
    print "Exiting..."
    exit 1
  fi

  AUTO_MOUNT $FIN_PARTITION_ROOT /mnt/funtoo

  if [ ! -e /mnt/funtoo ]; then
    print "error: '/mnt/funtoo' does not exist!"
    print "'/mnt/funtoo' is required for us to build" \
          "Funtoo, as it is referred to later in this script." \
          "Please ensure your mounted partitions are correct."
    print "Exiting..."
    exit 1
  fi
}

OUT_BOOTSTRAP() {
  cd /mnt/funtoo

  print "correct system time via ntpd... "
  ntpdate -s ntp.aliyun.com
  hwclock -w

  #if [ -n "$(command -v ntpd)" ]; then
  #    ntpd -q -g
  #else
  #    print "warning: ntpd not found!"
  #    print "assuming system time is correct"
  #fi

  print "Downloading and extracting Stage3 tarball..."
  if [ -n "$(command -v curl)" ]; then
    # stage3_src="https://build.funtoo.org/1.4-release-std/$FIN_CPU_DIR/$FIN_CPU_ARCH/stage3-latest.tar.xz"
    if [ ! -e "stage3-latest.tar.xz" ]; then
      curl -O "$stage3_src"
      # scp damon@192.168.0.104:/home/damon/share/stage3-latest.tar.xz .
      #curl -o stage3-latest.tar.xz http://192.168.3.240:8000/Gentoo/stage3-latest.tar.xz
    fi
    if [ ! -e "/mnt/funtoo/boot" ]; then
      tar -xpvf stage3-* --xattrs --numeric-owner -C /mnt/funtoo
      # rm -rf "$PWD/stage3-*"
    fi
    unset stage3_src
  else
    print "error: curl not present!"
    print "Exiting..."
    exit 1
  fi
}

OUT_DOWNLOAD_CONF() {
  mkdir -p /mnt/funtoo
  print "ensure script is available in /mnt/funtoo... "
  #if [ ! -e /mnt/funtoo/fin.sh ]; then
  cp -f fin.sh /mnt/funtoo/
  #fi
  cd /mnt/funtoo

  print "Downloading portage configuration files..."
  config_files="
        etc/portage/make.conf
        etc/portage/sets/fin-base
        etc/portage/sets/fin-i3
        etc/portage/sets/fin-nscde
    "

  for config_file in $config_files; do
    local config_dir="`dirname /mnt/funtoo/$config_file`"
    [ -e ${config_dir} ] && [ ! -d ${config_dir} ] && rm ${config_dir}
    mkdir -p ${config_dir}
    [ -e "/mnt/funtoo/$config_file" ] && rm -rf "/mnt/funtoo/$config_file"
    wget "$FIN_CONFIG_URL/$config_file" -O "/mnt/funtoo/$config_file"
  done; unset config_dir config_files config_file

  print "Updating make.conf..."
  sed -i "
        s/^MAKEOPTS.*$/MAKEOPTS\=\"-j$(grep -c ^processor /proc/cpuinfo)\"/;
        s/^VIDEO_CARDS.*$/VIDEO_CARDS=\"$FIN_VIDEO_CARDS\"/;
    " /mnt/funtoo/etc/portage/make.conf

  # echo "config portage:"
  if ! grep -q "sync_base_url" /mnt/funtoo/etc/ego.conf; then
    echo "sync_base_url = https://gitee.com/funtoo/{repo}" >> /mnt/funtoo/etc/ego.conf
  fi

  print "Copying '/etc/resolv.conf'..."
  # echo "nameserver 8.8.8.8" > /mnt/funtoo/etc/resolv.conf
  cp --dereference /etc/resolv.conf /mnt/funtoo/etc/
}

OUT_CHANGE_ROOT() {
  print "Mounting hardware devices..."
  cd /mnt/funtoo/
  for hw_mountpoint in $(echo proc sys dev); do
    if [ -e /mnt/funtoo/"$hw_mountpoint" ]; then
      case "$hw_mountpoint" in
        proc)
          mount -t proc /proc /mnt/funtoo/proc
          ;;

        sys)
          mount --rbind /sys /mnt/funtoo/sys
          mount --make-rslave /mnt/funtoo/sys
          ;;

        dev)
          mount --rbind /dev /mnt/funtoo/dev
          mount --make-rslave /mnt/funtoo/dev
          ;;

        *)
          print "error: $hw_mountpoint: Improper " \
                "hardware device"
          exit 1
      esac
    else
      print "error: $hw_mountpoint unable to be mounted!" \
            "Exiting..."
      exit 1
    fi
  done; unset hw_mountpoint

  print "Setting up swapfile..."
  if [ -v "$FIN_PARTITION_SWAP" ]; then
    if [ "$FIN_PARTITION_SWAP" = "/swapfile" ]; then
      fallocate -l "/mnt/funtoo/$FIN_PARTITION_SWAP_SIZE"
    fi

    mkswap "/mnt/funtoo/$FIN_PARTITION_SWAP"
    swapon "/mnt/funtoo/$FIN_PARTITION_SWAP"
    echo "$FIN_PARTITION_SWAP none swap sw 0 0" >> /mnt/funtoo/etc/fstab
  fi

  print "Chroot'ing into /mnt/funtoo..."
  local params=""
  if [ "$1" != "" ]; then
    params="-c '/fin.sh -i'"
  fi
  chroot \
    /mnt/funtoo /usr/bin/env -i \
    HOME="/root" \
    MANPATH="/usr/man:/usr/share/man:/usr/local/man:/usr/local/share/man" \
    MANPATH="$MANPATH:$HOME/man:$HOME/.local/man:$HOME/.local/share/man" \
    PS1="\[\e];\u@\h: \w\a\][\u@\h \W]\$ " \
    PATH="/sbin:/usr/sbin:/opt/sbin:/usr/local/sbin/:/bin:/usr/bin" \
    PATH="$PATH:/opt/bin:/usr/local/bin:$HOME/bin:$HOME/.local/bin" \
    TERM="$TERM" \
    /bin/bash --login # "${params}"
}

IN_INSTALL() {
  print "Updating environment..."
  env-update &&
    source /etc/profile
  export PS1="\[\e];\u@\h: \w\a\][\u@\h \W]\$ "

  print "Setting /etc/ftab..."
  lsblk
  sleep 5
  nano -w /etc/fstab

  print "Syncing Portage..."
  ego sync
  emerge -v --quiet-build net-vpn/openconnect

  print "Setting hostname..."
  echo "hostname=$FIN_HOSTNAME" > /etc/conf.d/hostname

  print "Setting timezone..."
  echo "$FIN_TIMEZONE" > /etc/timezone
  emerge --config sys-libs/timezone-data

  print "Setting locale..."
  echo "$FIN_LOCALE" > /etc/locale.gen &&
    locale-gen
  L="$(echo $FIN_LOCALE | awk -F '[-]' '{print $1}')"
  LL="$(eselect locale list | grep -i $L | awk -F '[][]' '{print $2}')"
  eselect locale set "$LL"

  print "Selecting a profile..."
  #eselect profile list | grep -Evi "dev|exp"
  #profile_target=""
  #while [ -z "$profile_target" ]; do
  #    read -p "Which profile?: " profile_target
  #done
  #eselect profile set "$profile_target"

  print "Updating @world"
  epro flavor desktop
  emerge --quiet-build @preserved-rebuild
  emerge -uvDN --quiet-build @world
  # emerge -auvDN @world

  print "Emerging base system packages..."
  #time emerge -v --quiet-build @fin-base
  emerge @fin-base --autounmask-write
  etc-update --automode -3
  emerge -v --quiet-build @fin-base

  print "Adding services to OpenRC..."
  #rc-service net.eth0 stop
  #rc-update del net.eth0 default
  rc-update add NetworkManager default
  rc-update add dhcpcd default
  rc-update add cronie default

  # "::for startx:"
  #rc-update add dbus default
  #rc-update add openrc-settingsd default
  #rc-update add elogind boot

  #if grep -Rqi 'intel' /proc/cpuinfo; then
  #    print "Emerging intel-microcode"
  # emerge -v --quiet-build intel-microcode
  # emerge -v intel-microcode
  #fi

  IN_INSTALL_KERNEL

  print "Setting root password..."
  passwd

  read -p "Setup a standard user? [y/N]: " setup_user
  if echo "$setup_user" | grep -iq "^y"; then
    print "Creating user account"
    read -p "Username: " username
    useradd -m -G wheel,audio,video -s /bin/bash "$username"
    passwd "$username"
    sed -i '/ALL ALL=(ALL) ALL/a\${username} ALL=(ALL) ALL' /etc/sudoers
  fi

  print "Installation complete."
}

AUTO_MOUNT() {
  local part=$1
  local point=$2

  mkdir -p $point
  local partname=`basename $part`
  local info=`lsblk | grep $partname`
  echo "info:$info"
  local str="`echo $info | cut -d ' ' -f 7`"
  echo "str:|${str##*( )}|"
  if [ "${str##*( )}" == "" ]; then
    mount $part $point
  fi
}

AUTO_MKFS() {
  local part=$1
  local point=$2
  umount $part
  mkfs $2 $3 $4 $5
}

IN_INSTALL_KERNEL() {
  print "Configuring Linux kernel..."
  cd /usr/src/linux-*
  if [ "$FIN_KERNEL_AUTOBUILD" = "true" ]; then
    emerge -v --quiet-build debian-sources
    print "Error: AutoKernel isn't true or false. Exiting..."
  fi

  cd /

  print "Installing GRUB to $FIN_PARTITION_BOOT..."
  AUTO_MOUNT /dev/sda1 /boot/efi
  time grub-install "$FIN_PARTITION_BOOT"
  ego boot update
}

case $1 in
  -b|--bootstrap)
    OUT_DEPS
    OUT_BOOTSTRAP
    OUT_DOWNLOAD_CONF
    OUT_CHANGE_ROOT -i
    ;;

  -i|--install)
    IN_INSTALL
    ;;

  -c|--chroot)
    OUT_DEPS
    mkdir -p /mnt/funtoo/boot/efi
    AUTO_MOUNT /dev/sda1 /mnt/funtoo/boot/efi
    OUT_DOWNLOAD_CONF
    OUT_CHANGE_ROOT
    ;;

  -d)
    OUT_DOWNLOAD_CONF ;;

  -push|push)
    ipaddr="192.168.3.240"
    todir="/home/damon/binpkgs/funtoo/"
    if [ "$2"=="1" ]; then
      todir="/run/media/damon/Ventoy/binpkgs/funtoo/"
    fi
    if [ "$3"!="" ]; then
      ipaddr="$3"
    fi
    rsync -avze ssh /var/cache/portage/packages/ damon@${ipaddr}:${todir}
    ;;

  -sync|sync)
    git pull
    cp -rf etc/* /etc/
    ;;

  -vpn)
    openconnect https://fmt1.link.ac.cn --authgroup smart ;;

  --kernel)
    IN_INSTALL_KERNEL ;;

  -i3|i3)
    ego sync
    emerge -auvDN --quiet-build @world
    epro flavor desktop
    emerge -v --quiet-build @preserved-rebuild
    emerge @fin-i3 --autounmask-write
    etc-update --automode -3
    emerge -v --quiet-build @fin-i3

    #rc-update add dbus default
    #rc-update add openrc-settingsd default
    #rc-update add elogind boot
    ;;

  -nscde|nscde)
    # ego sync
    # emerge -auvDN --quiet-build @world
    # epro flavor desktop
    # emerge -v --quiet-build @preserved-rebuild
    # emerge @fin-nscde --autounmask-write
    # etc-update --automode -3
    # emerge -v --quiet-build @fin-nscde

    rc-update add dbus default
    rc-update add openrc-settingsd default
    rc-update add elogind boot

    mkdir -p /etc/xdg/menus/
    if [ ! -d "/NsCDE/" ]; then
      git clone https://gitee.com/NsCDE/NsCDE.git /NsCDE --depth 1
      cd /NsCDE/
      ./Installer.ksh -w -n -i
    else
      cd /NsCDE/
      git pull
      ./Installer.ksh -w -n -u
    fi
    ;;

  *)
    echo "$OS_NAME minimal installation script"
    echo "  -b. --bootstrap    Bootstrap the stage3 tarball"
    echo "  -i, --install      Install Funtoo"
    echo "  -c, --chroot       Chroot to /dev/sda3, continue install..."
esac
